package com.socodethis.spaceex;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.MathUtils;
import com.socodethis.spaceex.math.cPoint;

//This class holds all the dungeons
//Just give it a name, and it will return 
public class worldGeneratorClass {
	private ArrayList <dungeonGenerator> maps = new ArrayList <dungeonGenerator>();
	private ArrayList <dungeonConnect> edges = new ArrayList <dungeonConnect>();
	private ArrayList <RegionGeneratorClass> regions = new ArrayList <RegionGeneratorClass>();
	private Random random = new Random(System.nanoTime());
	private int currentIndex =0;
	private boolean justWarped;
	private textureHolder texture = new textureHolder();
	simpleNode node;
	//Don't think we will ever need to create an empty world
	//worldGeneratorClass(){
	//	maps = new ArrayList <dungeonGenerator>();
	//	currentIndex = -1;
	//}
	public worldGeneratorClass(worldGeneratorClass world){
		for(dungeonGenerator map:world.maps){
			this.maps.add(map);
		}
		for(dungeonConnect edge:world.edges){
			this.edges.add(edge);
		}
		this.currentIndex = world.currentIndex;
		this.justWarped = world.justWarped;
	}
	
	public worldGeneratorClass(int size){
		int count = 0, randomChambers;
		for(int i = 0; i < size; i++){
			//int size, int regiontype, int indexStart
			randomChambers = random.nextInt( 4 )+3;
			regions.add(new RegionGeneratorClass(count==0?randomChambers+1:randomChambers, random.nextInt( 5 ), count));
			//regions.add(new RegionGeneratorClass(randomChambers, 6, count));
			if(count == 0){count++;}
			count+=randomChambers;
		}
		for(int i = 0; i < regions.size();i++){
			for(dungeonGenerator dungeons: regions.get(i).getMaps()){
				maps.add(dungeons);
			}
			for(dungeonConnect edge: regions.get(i).getEdges()){
				edges.add(edge);
			}
		}

		for(int i = 0; i < regions.size()-1;i++){
			edges.add(new dungeonConnect(
				    //               Entrance in j                                    Exit in j+1
					new simpleNode(regions.get(i+1).getIStart()-1,
							maps.get(regions.get(i+1).getIStart()-1).createExit(true)),
							new simpleNode(regions.get(i+1).getIStart(),
									maps.get(regions.get(i+1).getIStart()).createExit(false))
					));
		}
		for(int i = 0; i < maps.size(); i++){
			maps.get(i).setTiles(true);
			for(int k = 0; k < 10; k++){
				int X = (int) MathUtils.random(maps.get(i).getDimensions().getX()-1), 
					Y = (int) MathUtils.random(maps.get(i).getDimensions().getY()-1);
				//To make sure that the enemy can move where he is set
				while(maps.get(i).returnAnyProperties(new cPoint(X,Y))){
					X = (int) MathUtils.random(maps.get(i).getDimensions().getX()-1);
					Y = (int) MathUtils.random(maps.get(i).getDimensions().getY()-1);
				}
				if(i !=0){
					maps.get(i).addEnemy(addMapEnemies(new cPoint(X,Y),i));
				}
			}
		}
	}
	
	public void addRegion(){
		int count = 0, randomChambers;
		int currentMaps=maps.size();
		//This is here so that the 
		for(int i = 0; i < 1; i++){
			//int size, int regiontype, int indexStart
			randomChambers = random.nextInt( 4 )+3;
			regions.add(new RegionGeneratorClass(randomChambers, random.nextInt( 5 ), count));
			//regions.add(new RegionGeneratorClass(randomChambers, 5, count));
			count+=randomChambers;
		}
		//for(int i = 0; i < regions.size();i++){
		int k = regions.size()-1;
			for(dungeonGenerator dungeons: regions.get(k).getMaps()){
				maps.add(dungeons);
			}
			for(dungeonConnect edge: regions.get(k).getEdges()){
				edges.add(edge);
			}
		//}

		//for(int i = 0; i < regions.size()-1;i++){
		k = regions.size()-2;
			edges.add(new dungeonConnect(
				    //               Entrance in j                                    Exit in j+1
					new simpleNode(regions.get(k+1).getIStart()-1,
							maps.get(regions.get(k+1).getIStart()-1).createExit(true)),
							new simpleNode(regions.get(k+1).getIStart(),
									maps.get(regions.get(k+1).getIStart()).createExit(false))
					));
		//}
		for(int i = currentMaps; i < maps.size(); i++){
			maps.get(i).setTiles(true);
			for(int j = 0; j < 10; j++){
				int X = (int) MathUtils.random(maps.get(i).getDimensions().getX()-1), 
					Y = (int) MathUtils.random(maps.get(i).getDimensions().getY()-1);
				//To make sure that the enemy can move where he is set
				while(maps.get(i).returnAnyProperties(new cPoint(X,Y))){
					X = (int) MathUtils.random(maps.get(i).getDimensions().getX()-1);
					Y = (int) MathUtils.random(maps.get(i).getDimensions().getY()-1);
				}
				maps.get(i).addEnemy(addMapEnemies(new cPoint(X,Y),i));
			}
		}
	}
	
	public enemyClass addMapEnemies(cPoint Pos, int index){
		switch(random.nextInt(5)){
			case 0: return new enemyClass(new spriteAnimate(
				texture.returnEnemyRobot_lu(false),
				texture.returnEnemyRobot_lu(true),
				texture.returnEnemyRobot_ld(false),
				texture.returnEnemyRobot_ld(true)
				, Pos ,new cPoint(0,0),128,true,.08f),
				maps.get(index), new AttackLoader(50,20,.3f,1f,1,"friendBall"), false);
			case 1:return new enemyClass(new spriteAnimate(
				texture.returnEnemySpider_lu(false),
				texture.returnEnemySpider_lu(true),
				texture.returnEnemySpider_ld(false),
				texture.returnEnemySpider_ld(true)
				, Pos ,new cPoint(0,0),128,true,.08f),
				maps.get(index), new AttackLoader(50,20,.3f,1f,1,"friendBall"), false);
			case 2:return new enemyClass(new spriteAnimate(
				texture.returnEnemySkull_lu(false),
				texture.returnEnemySkull_lu(true),
				texture.returnEnemySkull_ld(false),
				texture.returnEnemySkull_ld(true)
				, Pos ,new cPoint(0,0),128,true,.08f),
				maps.get(index), new AttackLoader(50,20,.3f,1f,1,"friendBall"), false);
			case 3:return new enemyClass(new spriteAnimate(
				texture.returnEnemyMech_lu(false),
				texture.returnEnemyMech_lu(true),
				texture.returnEnemyMech_ld(false),
				texture.returnEnemyMech_ld(true)
				, Pos ,new cPoint(0,0),128,true,.08f),
				maps.get(index), new AttackLoader(50,20,.3f,1f,1,"friendBall"), false);
			case 4:return new enemyClass(new spriteAnimate(
				texture.returnEnemyTank_lu(false),
				texture.returnEnemyTank_lu(true),
				texture.returnEnemyTank_ld(false),
				texture.returnEnemyTank_ld(true)
				, Pos ,new cPoint(0,0),128,true,.08f),
				maps.get(index), new AttackLoader(50,20,.3f,4f,1,"earthBall"), true);
			default:return new enemyClass(new spriteAnimate(
					texture.returnEnemyRobot_lu(false),
					texture.returnEnemyRobot_lu(true),
					texture.returnEnemyRobot_ld(false),
					texture.returnEnemyRobot_ld(true)
					, Pos ,new cPoint(0,0),128,true,.08f),
					maps.get(index), new AttackLoader(50,20,.3f,1f,1,"friendBall"), false);
			}
	}

	public int getSize(){
		return maps.size();
	}
	//Shouldn't be used. All maps should be instantiated at the beginning
	//public void createMap(int index, int width, int height){
	//	maps.add(new dungeonGenerator(index, width, height));
	//	currentIndex++;
	//}
	public boolean canGoThere(int index, cPoint position){
		//position.printPoint();
		//maps.get(index).getDimensions().printPoint();
		if(!position.inSquare(0, 0, maps.get(index).getDimensions().getX()-1, maps.get(index).getDimensions().getY()-1)){
			return false;
		}
		if(!maps.get(index).returnPassable(position)){
			return false;
		}
		return true;
	}
	public int returnPos(int index, cPoint position){
		return maps.get(index).returnPassable(position)?1:0;
	}
	public boolean isExit(int index, cPoint position){
		return maps.get(index).returnExit(position);
	}
	public SpriteProperties getFloorProps(int index, cPoint position){
		return maps.get(index).getFloorProperties(position).getFloor();
	}
	public SpriteProperties getStructureProps(int index, cPoint position){
		return maps.get(index).getFloorProperties(position).getCurrentStructure();
	}
	public SpriteProperties getStructureDirt(int index, cPoint position){
		return maps.get(index).getFloorProperties(position).getDirt();
	}
	public void setCurrentIndex(int index){
		this.currentIndex = index;
	}
	public int getIndex(){
		return currentIndex;
	}
	public dungeonGenerator getDungeon(int index){
		return maps.get(index);
	}
	public boolean justWarped(){
		return justWarped;
	}
	public void warpExpired(){
		justWarped = false;
	}
	public cPoint checkExit(int index, cPoint position){
		node = new simpleNode(index, position);
		//Moves through all the edges in the dungeon world to see if the exit is attached to anything
		for(dungeonConnect dungeons: edges){
			//if(dungeons.hasDungeon(index)){
			//	dungeons.printConnection();
			//}
			//dungeons.printConnection();
			//If there is a match, the node is switched to the opposing exit in another dungeon
			if (dungeons.getEquals(node)){
				//dungeons.printConnection();
				node = new simpleNode(dungeons.getConnect(node));
				justWarped = true;
			}
			//If there is no match, nothing happens
		}
		//Marks the new current dungeon
		this.currentIndex = node.getDungeon();
		//Returns the new player position
		return node.getExit();
	}
	//This function returns the amount of connections a given dungeon has
	public int getConnections(int dungeon){
		int count = 0;
		for(dungeonConnect dungeons:edges){
			if(dungeons.hasDungeon(dungeon)){
				count++;
			}
		}
		return count;
	}
	public Color getColor(){
		return maps.get(currentIndex).getColor();
	}
	//This function returns two random dungeons attached to the given dungeon
	public int[] getRDungs(int dungeon){
		//Shuffles the edges
		int[] rDungs = new int[]{0,1};
		int count = 0;
		long seed = System.nanoTime();
		Collections.shuffle(edges, new Random(seed));
		for(dungeonConnect dungeons:edges){
			if(dungeons.hasDungeon(dungeon)){
				rDungs[count] = dungeons.getConnect(dungeon);
				if(rDungs.length > 1){
					//This is to make sure that the dungeon does not look back onto itself
					if(rDungs[0]!=rDungs[1]){
						count++;
					}
				}else{
					count++;
				}
				if(count > 1){
					return rDungs;
				}
			}
		}
		return rDungs;

	}
}
